#pragma once
#include <string>
#include <vector>
#include <sstream>
#include <memory>

using namespace std;

struct ParseTree
{
    enum OperatorType
    {
        Token,
        AndThen,
        OrThen,
        Repetition,
        ParserRule
    };


    OperatorType opType;
    string name;

    shared_ptr<ParseTree> left;
    shared_ptr<ParseTree> right;

//    ParseTree* left;
//    ParseTree* right;
    int minCount;
    int maxCount;

    ParseTree(OperatorType opTypeIn, const string& nameIn, shared_ptr<ParseTree> leftIn, shared_ptr<ParseTree> rightIn, int minCountIn, int maxCountIn)
    : opType(opTypeIn), name(nameIn), left(leftIn), right(rightIn), minCount(minCountIn), maxCount(maxCountIn)
    {

    }

     string toString()
     {
         stringstream ss;

         switch (opType)
         {
            case Token:
                ss << name;
                break;
            case AndThen:
                ss << "("<<left->toString()<< " >> " << right->toString() <<")";
                break;
            case OrThen:
                ss << "("<<left->toString()<< " | " << right->toString() <<")";
                break;
            case Repetition:
                ss << "rep("<< minCount << ", " << maxCount << ", " << left->toString() << ")";
                break;
            case ParserRule:
                ss << name;
                break;
         }
         return ss.str();
     }
};

struct ParserRule
{
    string name;
    ParseTree parseTree;

    ParserRule (string nameIn, const ParseTree& treeIn)
    : name(nameIn), parseTree(treeIn)
    {

    }
};

shared_ptr<ParseTree> rep(int minIn, int maxIn, const shared_ptr<ParseTree>& tree)
{
    shared_ptr<ParseTree> newTree(new ParseTree(ParseTree::OperatorType::Repetition, "", tree, nullptr, minIn, maxIn));
    return newTree;
}

shared_ptr<ParseTree> tk(const string& nameIn)
{
    shared_ptr<ParseTree> newTree(new ParseTree(ParseTree::OperatorType::Token, nameIn, nullptr, nullptr, 1, 1));
    return newTree;
}


shared_ptr<ParseTree> operator>>(const shared_ptr<ParseTree>& left, const shared_ptr<ParseTree>& right)
{
    shared_ptr<ParseTree> newTree(new ParseTree(ParseTree::OperatorType::AndThen, "", left, right, 1, 1));
    return newTree;
}

shared_ptr<ParseTree> operator|(const shared_ptr<ParseTree>& left, const shared_ptr<ParseTree>& right)
{
    shared_ptr<ParseTree> newTree(new ParseTree(ParseTree::OperatorType::OrThen, "", left, right, 1, 1));
    return newTree;
}

shared_ptr<ParseTree> operator*(const shared_ptr<ParseTree>& left)
{
    shared_ptr<ParseTree> newTree(new ParseTree(ParseTree::OperatorType::Repetition, "", left, nullptr, 0, -1));
    return newTree;
}
